//代码题
// 一、将下面异步代码使用 Promise 的方式改进
// setTimeout(function () {
//   var a = 'hello'
//   setTimeout(function () {
//     var b = 'lagou'
//     setTimeout(function () {
//       var c = 'I Love U'
//       console.log(a + b + c)
//     }, 10)
//   }, 10)
// }, 10)


// function setTimOut(value){
//     return new Promise((resolve,reject) =>{
//         setTimeout(()=>{
//             resolve(value)
//         },10)
//     })
// }
//
//
// setTimOut('hello')
//     .then(value => {
//         return value+=' lagou '
//     }).then(value => {
//         value+=' I ♥ U'
//         console.log(value)
//     })

// 二、基于以下代码完完成四个练习
const fp= require('lodash/fp')
//horsepower马力    dollar_value价格  in_stock 库存
const cars = [
{name:'Ferrari FF',horsepower:660,dollar_value:700000,in_stock:true},
{name:'Spyker C12 Zagato',horsepower:650,dollar_value:648000,in_stock:false},
{name:'Jaguar XKR-S',horsepower:550,dollar_value:132000,in_stock:false},
{name:'Audi R8',horsepower:525,dollar_value:1142000,in_stock:false},
{name:'Aston Martin One-77',horsepower:750,dollar_value:1850000,in_stock:true},
{name:'Pagani Huayra',horsepower:700,dollar_value:1300000,in_stock:false}]

//练习1 使用函数组合 fp.flowRight()实现
const isLastInStock = fp.flowRight(fp.prop('in_stock'),fp.last)
let InStock = isLastInStock(cars)
console.log(InStock)

//练习2 获取第一个car的name
const isFirstNanme = fp.flowRight(fp.prop('name'),fp.first)
let Name = isFirstNanme(cars)
console.log(Name)

//练习3 使用_average重构averageDollarValue，使用函数组合的方式实现
// let averageDollarValue = function (cars) {
//     let dollar_value = fp.map(function (car) {
//         return car.dollar_value
//
//     },cars)
//     return _average(dollar_value)
// }
// console.log(averageDollarValue(cars))
let _average = function (xs) {
    return fp.reduce(fp.add,0,xs)/xs.length
}
const averageDollarValue = fp.flowRight(_average,fp.map(car=>car.dollar_value))
let ADV = averageDollarValue(cars)
console.log(ADV)

//练习4 使用flowRight写一个sanitizeNames()函数，返回一个下划线连接的小写字符串，把数组中的name转换为这种形式：例如
        // sanitizeNames(['Hello World]) => ['hellow_word']
let _underscore = fp.replace(/\W+/g,'_')
const name = ['Hello World']
let sanitizeNames = fp.map(fp.flowRight(fp.toLower, _underscore))
console.log(sanitizeNames(name))



//三、基于下面的代码，完成后续联系
const {Maybe,Container} = require('./support')
//练习1 使用fp.add(x,y)和fp.map(f,x)创建一个能让functor里的值增减的函数ex1
let maybe = Maybe.of([5,6,1])
let ex1 = maybeValue=>{
    console.log(fp.map(value =>fp.add(value,1),maybeValue))
}
ex1(maybe._value)

//练习2 实现一个函数ex2，能够使用fp.first获取列表的第一个元素
let xs = Container.of(['do','ray','me','fa','so','la','ti','do'])
let ex2 = value =>{
    console.log(fp.first(value))
}
ex2(xs._value)

//练习3 实现一个函数ex3,使用safeProp和fp.first找到user的名字的首字母
let safeProp = fp.curry(function (x,o) {
    return Maybe.of(o(x))
})
let user = {id:2,name:'Albert'}
let ex3 = () =>{
    console.log(safeProp(user.name,fp.first)._value)
}
ex3()

//练习4 使用Maybe重写ex4 ，不要使用if语句
// let ex4 = function (n) {
//     if (n){
//         return parseInt(n)
//     }
//
// }
let ex4 = n => Maybe.of(n).map(parseInt)._value
console.log(ex4('123'))


//四、手写 MyPromise源码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise{
    constructor(executor) {
        // 捕获执行器executor中的代码错误
        try {
            executor(this.resolve, this.reject);//执行器接收两个函数
        } catch (e) {
            this.reject(e);
        }
    }
    //promsie状态   默认  等待
    status = PENDING
    //成功之后的值
    value=undefined
    //失败之后的值
    reason = undefined
    // 成功回调
    successCallback = []
    // 失败回调
    failCallback = []
    resolve = value =>{
        //状态不是PENDING不能改变，阻止向下执行
        if (this.status!=PENDING)return;
        //修改状态为成功
        this.status = FULFILLED
        //保存成功的值
        this.value = value
        //判断成功回调是否存在，存在则调用
        while (this.successCallback.length) this.successCallback.shift()()
    }
    reject = reason =>{
        //状态不是PENDING不能改变，阻止向下执行
        if (this.status!=PENDING)return;
        //修改状态为失败
        this.status = REJECTED
        //保存失败的值
        this.reason = reason
        //判断失败回调是否存在，存在则调用
        while (this.failCallback.length) this.failCallback.shift()()
    }
    then(successCallback,failCallback){
        // then 方法的链式调用返回 promise 对象
        successCallback = successCallback ? successCallback : value => value
        failCallback = failCallback ? failCallback : reason => { throw reason }
        let promise2 = new MyPromise((resolve,reject) =>{
            //判断状态
            if(this.status==FULFILLED){
                setTimeout(() => {
                    try {
                        // 后面 then 方法的回调函数拿到的值是上一个 then 方法的回调函数的返回值
                        let x = successCallback(this.value)
                        // 判断 x 的值是普通值还是 promise 对象
                        // 如果是普通值，直接调用 resolve
                        // 如果是 promise 对象，查看 promise 对象返回的结果
                        // 再根据 promise 对象返回的结果，决定调用 resolve 还是 reject

                        // 使用异步代码，获取到 promise2
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            }else if (this.status==REJECTED){
                setTimeout(() => {
                    try {
                        // 后面 then 方法的回调函数拿到的值是上一个 then 方法的回调函数的返回值
                        let x = failCallback(this.reason)
                        // 判断 x 的值是普通值还是 promise 对象
                        // 如果是普通值，直接调用 resolve
                        // 如果是 promise 对象，查看 promise 对象返回的结果
                        // 再根据 promise 对象返回的结果，决定调用 resolve 还是 reject

                        // 使用异步代码，获取到 promise2
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0)
            }else {
                // 等待
                // 将成功回调和失败回调存储起来
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            // 后面 then 方法的回调函数拿到的值是上一个 then 方法的回调函数的返回值
                            let x = successCallback(this.value)
                            // 判断 x 的值是普通值还是 promise 对象
                            // 如果是普通值，直接调用 resolve
                            // 如果是 promise 对象，查看 promise 对象返回的结果
                            // 再根据 promise 对象返回的结果，决定调用 resolve 还是 reject

                            // 使用异步代码，获取到 promise2
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            // 后面 then 方法的回调函数拿到的值是上一个 then 方法的回调函数的返回值
                            let x = failCallback(this.reason)
                            // 判断 x 的值是普通值还是 promise 对象
                            // 如果是普通值，直接调用 resolve
                            // 如果是 promise 对象，查看 promise 对象返回的结果
                            // 再根据 promise 对象返回的结果，决定调用 resolve 还是 reject

                            // 使用异步代码，获取到 promise2
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0)
                })
            }
        })
        return promise2
    }

    // 只执行失败回调,并返回新的promise
    catch(failCallback) {
        return this.then(undefined, failCallback)
    }


    // 无论前面promise 对象最终是成功还是失败，都会被调用
    finally(callback) {
        return this.then((value) => {
            return MyPromise.resolve(callback()).then(() => value)
        }, reason => {
            return MyPromise.resolve(callback()).then(() => { throw reason })
        })
    }

    // all返回的也是promise,全部执行成功返回结果，一个失败返回最先失败的值
    static all(array) {
        let result = []
        let index = 0;

        return new MyPromise((resolve, reject) => {
            function addData(key, value) {
                result[key] = value
                index++
                if (index === array.length) {
                    resolve(result)
                }
            }
            for (let i = 0; i < array.length; i++) {
                let current = array[i]
                // 判断是普通值还是promise
                if (current instanceof MyPromise) {
                    // promise对象
                    current.then(value => addData(i, value), reason => reject(reason))
                } else {
                    // 普通值直接resolve并返回该值
                    addData(i, current)
                }
            }
        })
    }

    // resolve val为promise则直接返回,普通值包装为promise
    static resolve(value) {
        if (value instanceof MyPromise) return value
        return new MyPromise((resolve) => resolve(value))
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    // 判断是否循环调用，是则reject
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    // 判断是否是promise
    if (x instanceof MyPromise) {
        x.then(resolve, reject)
    } else {
        resolve(x)
    }
}



let promise = new MyPromise((resolve,reject) =>{
    resolve('成功');
    // reject('失败');
})
// promise.then(value=>{
// 	console.log(1)
// 	console.log(value);
// },error=>{
// 	console.log(error);
// })

//
// promise.then(value=>{
// 	console.log(value);
// 	return 100;
// }).then(value=>{
// 	console.log(value);
// })

// function p1() {
//     return new MyPromise((resolve, reject) => {
//         setTimeout(() => {
//             resolve('p1成功');
//         }, 2000)
//         // reject('失败');
//     })
// }
// function p2() {
//     return new MyPromise((resolve, reject) => {
//         // resolve('p2成功');
//         reject('p2失败');
//     })
// }
// p2().finally(()=>{
// 	console.log('finally')
// 	return p1();
// }).then((value)=>{
// 	console.log(value)
// },(error)=>{
// 	console.log(error)
// })
// p2().then(value=>{
// 	console.log(value)
// }).catch(error=>{
// 	console.log(error)
// })
